package com.family.gmall.cart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.family.gmall.cart.mapper.CartInfoMapper;
import com.family.gmall.cart.service.CartService;
import com.family.gmall.common.constant.RedisConst;
import com.family.gmall.common.util.DateUtil;
import com.family.gmall.model.order.CartInfo;
import com.family.gmall.model.product.entity.SkuInfo;
import com.family.gmall.product.client.ProductFeignClient;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 购物车表 用户登录系统时更新冗余 服务实现类
 * </p>
 *
 * @author family
 * @since 2023-07-27
 */
@Service
public class CartServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartService {

    @Resource
    private CartInfoMapper cartInfoMapper;

    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private RedisTemplate redisTemplate;

    // @Resource
    // private CartAsyncService cartAsyncService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        //1. 添加商品之前先看一看购物车中是否有该商品
        String cartKey = this.getCartKey(userId);
        if (!redisTemplate.hasKey(cartKey)) {
            loadCartCache(userId);
        }
        CartInfo cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());
        if (cartInfoExist == null) {
            LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CartInfo::getUserId, userId);
            queryWrapper.eq(CartInfo::getSkuId, skuId);
            cartInfoExist = cartInfoMapper.selectOne(queryWrapper);
        }
        //true, 更新数量
        if (cartInfoExist != null) {
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum() + skuNum);
            cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
            cartInfoExist.setUpdateTime(new Date());
            cartInfoExist.setIsChecked(1);
            cartInfoMapper.updateById(cartInfoExist);
        } else {
            //  第一次添加购物车
            CartInfo cartInfo = new CartInfo();
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            //  在初始化的时候，添加购物车的价格 = skuInfo.price
            cartInfo.setCartPrice(skuInfo.getPrice());
            //  数据库不存在的，购物车的价格 = skuInfo.price
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());
            cartInfo.setIsChecked(1);
            //  执行数据库操作
            cartInfoMapper.insert(cartInfo);
            cartInfoExist = cartInfo;
        }
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfoExist);
        this.setCartKeyExpire(cartKey);
    }

    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        // 什么一个返回的集合对象
        List<CartInfo> cartInfoList = new ArrayList<>();
        // 未登录：临时用户Id 获取未登录的购物车数据
        if (StringUtils.isEmpty(userId)) {
            cartInfoList = this.getCartList(userTempId);
            return cartInfoList;
        }
    /*
         1. 准备合并购物车
         2. 获取未登录的购物车数据
         3. 如果未登录购物车中有数据，则进行合并 合并的条件：skuId 相同 则数量相加，合并完成之后，删除未登录的数据！
         4. 如果未登录购物车没有数据，则直接显示已登录的数据
          */
        //已登录
        if (!StringUtils.isEmpty(userId)) {
            List<CartInfo> cartInfoArrayList = this.getCartList(userTempId);
            if (!CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 如果未登录购物车中有数据，则进行合并 合并的条件：skuId 相同
                cartInfoList = this.mergeToCartList(cartInfoArrayList, userId);
                // 删除未登录购物车数据
                this.deleteCartList(userTempId);
            }

            // 如果未登录购物车中没用数据！
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 根据什么查询？userId
                cartInfoList = this.getCartList(userId);
            }
        }
        return cartInfoList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        CartInfo cartInfo = new CartInfo();
        cartInfo.setIsChecked(isChecked);
        LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CartInfo::getUserId, userId);
        queryWrapper.eq(CartInfo::getSkuId, skuId);
        cartInfoMapper.update(cartInfo, queryWrapper);

        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())) {
            CartInfo cartInfoUpd = hashOperations.get(skuId.toString());
            cartInfoUpd.setIsChecked(isChecked);
            hashOperations.put(skuId.toString(), cartInfoUpd);
            this.setCartKeyExpire(cartKey);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCart(Long skuId, String userId) {
        String cartKey = getCartKey(userId);
        cartInfoMapper.delete(new LambdaQueryWrapper<CartInfo>()
                .eq(CartInfo::getUserId, userId)
                .eq(CartInfo::getSkuId, skuId));
        //获取缓存对象
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())) {
            hashOperations.delete(skuId.toString());
        }
    }

    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        if (null != cartCachInfoList && cartCachInfoList.size() > 0) {
            cartCachInfoList.forEach(cartInfo -> {
                if (cartInfo.getIsChecked() == 1) {
                    cartInfoList.add(cartInfo);
                }
            });
        }
        return cartInfoList;
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteCartList(String userId) {
        cartInfoMapper.delete(new LambdaQueryWrapper<CartInfo>().eq(CartInfo::getUserId, userId));
        String cartKey = getCartKey(userId);
        if (redisTemplate.hasKey(cartKey)) {
            redisTemplate.delete(cartKey);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNoLoginList, String userId) {
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);
        Map<Long, CartInfo> longCartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        for (CartInfo cartInfo : cartInfoNoLoginList) {
            Long skuId = cartInfo.getSkuId();
            if (longCartInfoMap.containsKey(skuId)) {
                CartInfo cartInfoLogin = longCartInfoMap.get(skuId);
                cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfo.getSkuNum());
                cartInfoLogin.setUpdateTime(new Date());
                if (1 == cartInfo.getIsChecked().intValue()) {
                    cartInfoLogin.setIsChecked(1);
                }
                LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(CartInfo::getUserId, cartInfoLogin.getUserId());
                queryWrapper.eq(CartInfo::getSkuId, cartInfoLogin.getSkuId());
                cartInfoMapper.update(cartInfoLogin, queryWrapper);
            } else {
                cartInfo.setUserId(userId);
                cartInfo.setCreateTime(new Date());
                cartInfo.setUpdateTime(new Date());
                cartInfoMapper.insert(cartInfo);
            }
        }
        List<CartInfo> cartInfoList = this.loadCartCache(userId);
        return cartInfoList;
    }


    /**
     * 根据用户获取购物车
     *
     * @param userId
     * @return
     */
    public List<CartInfo> getCartList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (StringUtils.isEmpty(userId)) return cartInfoList;
        String cartKey = this.getCartKey(userId);
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil
                            .truncatedCompareTo(
                                    o2.getUpdateTime(),
                                    o1.getUpdateTime(),
                                    Calendar.SECOND);
                }
            });
        } else {
            cartInfoList = loadCartCache(userId);
        }
        return cartInfoList;
    }

    public List<CartInfo> loadCartCache(String userId) {
        LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CartInfo::getUserId, userId);
        queryWrapper.orderByDesc(CartInfo::getUpdateTime);
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }
        // 将数据库中的数据查询并放入缓存
        HashMap<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
            cartInfo.setSkuPrice(skuPrice);
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }
        // 定义key user:userId:cart
        String cartKey = this.getCartKey(userId);
        redisTemplate.opsForHash().putAll(cartKey, map);
        // 设置过期时间
        this.setCartKeyExpire(cartKey);
        return cartInfoList;

    }


    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }
}
